package com.atguigu.gmall.activity.service.impl;

import com.atguigu.gmall.activity.mapper.SeckillGoodsMapper;
import com.atguigu.gmall.activity.service.SeckillGoodsService;
import com.atguigu.gmall.activity.util.CacheHelper;
import com.atguigu.gmall.common.constant.RedisConst;
import com.atguigu.gmall.common.result.Result;
import com.atguigu.gmall.common.result.ResultCodeEnum;
import com.atguigu.gmall.common.util.MD5;
import com.atguigu.gmall.model.activity.OrderRecode;
import com.atguigu.gmall.model.activity.SeckillGoods;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;

import java.util.List;
import java.util.concurrent.TimeUnit;

/**
 * @author mqx
 * @date 2020-11-25 10:48:27
 */
@Service
public class SeckillGoodsServiceImpl implements SeckillGoodsService {

    //  注入mapper！
    @Autowired
    private RedisTemplate redisTemplate;

    @Autowired
    private SeckillGoodsMapper seckillGoodsMapper;

    @Override
    public List<SeckillGoods> findAll() {
        //  获取缓存中所有秒杀商品的数据 hash ,hset (key,field,value) hget(key,field)
        List<SeckillGoods> seckillGoodsList = redisTemplate.boundHashOps(RedisConst.SECKILL_GOODS).values();
        return seckillGoodsList;
    }

    @Override
    public SeckillGoods getSeckillGoods(Long skuId) {
        //  hget(key,field)
        SeckillGoods seckillGoods = (SeckillGoods) redisTemplate.boundHashOps(RedisConst.SECKILL_GOODS).get(skuId.toString());
        return seckillGoods;
    }

    @Override
    public void seckillOrder(String userId, Long skuId) {
        //  校验状态位：  存储数据：key = skuId value = 状态位
        String state  = (String) CacheHelper.get(skuId.toString());
        //  1 : 有商品，可以下单，0:没有商品不能下单
        if ("0".equals(state)){
            return;
        }
        //  判断用户是否已经下过订单 使用setnx() 如果用户下过订单了，那么会将数据存储到缓存！
        //  缓存存储的数据类型，以及key！RedisConst.SECKILL_USER + userId  || seckill:user:userId  value = skuId
        //  set(seckill:user:userId ,skuId) String 数据类型
        Boolean flag = redisTemplate.opsForValue().setIfAbsent(RedisConst.SECKILL_USER + userId, skuId.toString(), RedisConst.SECKILL__TIMEOUT, TimeUnit.SECONDS);
        //  判断flag=true 表示第一次购买，如果是false 则用户已经下过订单
        if (!flag){
            return;
        }
        //  对应减库存   seckill:stock:31
        String skuIdIsExist = (String) redisTemplate.boundListOps(RedisConst.SECKILL_STOCK_PREFIX + skuId.toString()).rightPop();
        //  如果skuIdIsExist 获取到的不是空，则说明秒杀成功！反之
        if (StringUtils.isEmpty(skuIdIsExist)){
            //  如果商品的状态位是0，则需要通知其他兄弟节点更新状态位！
            redisTemplate.convertAndSend("seckillpush",skuId+":0");
            return;
        }
        //  如果能够下单：将数据保存到 OrderRecode 秒杀订单记录
        OrderRecode orderRecode = new OrderRecode();
        //  一顿赋值
        orderRecode.setUserId(userId);
        //  表示下单码
        orderRecode.setOrderStr(MD5.encrypt(userId+skuId));
        orderRecode.setNum(1);
        orderRecode.setSeckillGoods(this.getSeckillGoods(skuId));

        //  将数据放入缓存！    数据类型Hash + key  RedisConst.SECKILL_ORDERS
        //  key = seckill:orders field = userId value = orderRecode
        redisTemplate.boundHashOps(RedisConst.SECKILL_ORDERS).put(userId,orderRecode);

        //  更新库存！   缓存 + mysql
        this.updateStockCount(skuId);
    }

    @Override
    public Result checkOrder(Long skuId, String userId) {
        //  判断用户是否在缓存中存在
        //  RedisConst.SECKILL_USER + userId
        Boolean isExist = redisTemplate.hasKey(RedisConst.SECKILL_USER + userId);
        if (isExist){
            //  这个key 存在的时候，才有可能抢购成功！
            //  RedisConst.SECKILL_ORDERS
            Boolean isHasKey = redisTemplate.boundHashOps(RedisConst.SECKILL_ORDERS).hasKey(userId);
            //  表示有预下单的key！
            //  预下单：指在排队页面中抢购成功。    下单：在下单页面中填写用户收货信息等。
            if (isHasKey){
                //  需要使用build   hget(key,field) =
                OrderRecode orderRecode = (OrderRecode) redisTemplate.boundHashOps(RedisConst.SECKILL_ORDERS).get(userId);
                //  表示秒杀成功！
                return Result.build(orderRecode, ResultCodeEnum.SECKILL_SUCCESS);
            }
        }

        //  判断用户是否下过订单 ：如果用户下单成功，那么缓存应该如何存储数据！
        //  下单成功：保存的数据 hset(key,field,value)   key = RedisConst.SECKILL_ORDERS_USERS   field = userId value = orderId
        Boolean isExistOrder = redisTemplate.boundHashOps(RedisConst.SECKILL_ORDERS_USERS).hasKey(userId);
        //  如果返回true:   表示已经下过订单
        if (isExistOrder){
            String orderId = (String) redisTemplate.boundHashOps(RedisConst.SECKILL_ORDERS_USERS).get(userId);
            //  表示秒杀成功，去看我的订单！
            return Result.build(orderId, ResultCodeEnum.SECKILL_ORDER_SUCCESS);
        }

        //  判断状态位：  0 表示已经售罄，1 表示可以抢购
        //  校验状态位：  存储数据：key = skuId value = 状态位
        String state  = (String) CacheHelper.get(skuId.toString());
        if ("0".equals(state)){
            //  表示当前商品已经售罄，抢单失败！
            return Result.build(null, ResultCodeEnum.SECKILL_FAIL);
        }
        //  其他情况就是排队中。。。。。
        return Result.build(null, ResultCodeEnum.SECKILL_RUN);
    }

    //  更新库存！
    private void updateStockCount(Long skuId) {
        //  获取剩余库存的数量  List = seckill:stock:31
        Long count = redisTemplate.boundListOps(RedisConst.SECKILL_STOCK_PREFIX + skuId.toString()).size();
        //  为了避免频繁更新数据库，可以做个操作！
        if (count%2==0){
            //  更新数据库
            //            SeckillGoods seckillGoods = new SeckillGoods();
            //            seckillGoods.setStockCount(count.intValue());
            //            QueryWrapper<SeckillGoods> seckillGoodsQueryWrapper = new QueryWrapper<>();
            //            seckillGoodsQueryWrapper.eq("sku_id",skuId);
            //            seckillGoodsMapper.update(seckillGoods,seckillGoodsQueryWrapper);

            //  获取缓存的对象 是一个有 Id的SeckillGoods
            SeckillGoods seckillGoodsRedis = this.getSeckillGoods(skuId);
            seckillGoodsRedis.setStockCount(count.intValue());
            //  更新数据库
            seckillGoodsMapper.updateById(seckillGoodsRedis);
            //  更新缓存！
            //  RedisConst.SECKILL_GOODS; seckill:goods
            redisTemplate.boundHashOps(RedisConst.SECKILL_GOODS).put(skuId.toString(),seckillGoodsRedis);
        }
    }


}
